package reesoft.idgenerator.service.idstore;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reesoft.certificate.agent.CertificateAgent;
import reesoft.common.code.ErrorCode;
import reesoft.common.entity.Result;
import reesoft.common.util.ApplicationContextUtil;
import reesoft.common.util.HttpUtil;
import reesoft.common.util.StringUtil;
import reesoft.idgenerator.IdGenerateException;
import reesoft.idgenerator.IdGeneratorErrorCode;
import reesoft.idgenerator.OnlyPartialIdAssignedException;
import reesoft.idgenerator.entity.IdRange;
import reesoft.idgenerator.entity.NextBatchIdRequest;
import reesoft.idgenerator.entity.RegisterIdRequest;

/**
 * 在服务器存储序号，通过 HTTP 协议提供服务
 *
 * Created by reesoft on 2017-08-28
 */
public class HttpRemoteIdStore extends BaseIdStore
{
    private static final Logger LOG = LoggerFactory.getLogger(HttpRemoteIdStore.class);

    private final HttpRemoteIdStoreConfig config = ApplicationContextUtil.getBean(HttpRemoteIdStoreConfig.class);

    private final CertificateAgent certificateAgent = ApplicationContextUtil.getBean(CertificateAgent.class);

    @Override
    public void init(String name, long initValue, long maxValue) throws IdGenerateException
    {
        LOG.debug("HttpRemoteIdStore.init, name:{}, initValue:{}, maxValue:{}", name, initValue, maxValue);

        if (config == null
                || StringUtil.isBlank(config.getInitUrl()))
        {
            throw new IdGenerateException("Server address is not configured");
        }

        if (StringUtil.isBlank(name))
        {
            throw new IdGenerateException(ErrorCode.INVALID_PARAMETER);
        }

        try
        {
            RegisterIdRequest registerIdRequest = new RegisterIdRequest(name, initValue, maxValue);
            registerIdRequest.setCommand("register");

            Result result = certificateAgent.signRequest(registerIdRequest);

            if (!result.isSuccess())
            {
                throw new IdGenerateException("Failed to sign request data");
            }

            String url = config.getInitUrl() + registerIdRequest.toParameterString();

            result = JSON.parseObject(HttpUtil.get(url), Result.class);

            if (result == null)
            {
                throw new IdGenerateException("Failed to get response from server");
            }

            if (!IdGeneratorErrorCode.SUCCESS.equals(result.getCode()))
            {
                throw new IdGenerateException(result.getCode(), result.getMessage());
            }
        }
        catch (Exception e)
        {
            throw new IdGenerateException(e.getMessage());
        }
    }

    @Override
    public boolean check(String name)
    {
        //LOG.debug("HttpRemoteIdStore.check, name:" + name);

        if (config == null
                || StringUtil.isBlank(config.getInitUrl()))
        {
            return false;
        }

        try
        {
            String url = config.getCheckUrl() + "?name=" + name;

            Result response = JSON.parseObject(HttpUtil.get(url), Result.class);

            if (response != null)
            {
                return IdGeneratorErrorCode.SUCCESS.equals(response.getCode());
            }
        }
        catch (Exception e)
        {
            LOG.error(e.toString());
        }

        return false;
    }

    @Override
    public long loadAndIncrease(String name, long amount) throws IdGenerateException
    {
        LOG.info("HttpRemoteIdStore.loadAndIncrease, name:{}, amount:{}", name, amount);

        if (config == null
                || StringUtil.isBlank(config.getInitUrl()))
        {
            throw new IdGenerateException("Server address is not configured");
        }

        if (StringUtil.isBlank(name)
                || amount <= 0)
        {
            throw new IdGenerateException(ErrorCode.INVALID_PARAMETER);
        }

        try
        {
            NextBatchIdRequest nextBatchIdRequest = new NextBatchIdRequest(name, amount);
            nextBatchIdRequest.setCommand("nextBatch");

            Result result = null;

            for (int tryCount = 0; tryCount < config.getMaxTryCount(); tryCount++)
            {
                result = certificateAgent.signRequest(nextBatchIdRequest);

                LOG.info("sign result:{}", result.getCode());

                if (!result.isSuccess())
                {
                    throw new IdGenerateException("Failed to sign data");
                }

                String url = config.getNextBatchUrl() + nextBatchIdRequest.toParameterString();

                String response = HttpUtil.get(url);

                result = JSON.parseObject(response, Result.class);

                if (result != null
                        && ErrorCode.SUCCESS.equals(result.getCode()))
                {
                    break;
                }

                // retry
            }

            if (result == null)
            {
                throw new IdGenerateException("Failed to get response from server");
            }

            if (!ErrorCode.SUCCESS.equals(result.getCode())
                    || result.getData() == null)
            {
                throw new IdGenerateException(result.getCode(), "Failed to call server");
            }

            JSONObject data = (JSONObject) result.getData();
            long startValue = Long.parseLong(data.get("startValue").toString());
            long amount_ = Long.parseLong(data.get("amount").toString());

            if (amount_ >= amount)
            {
                return Long.parseLong(data.get("startValue").toString());
            }
            else
            {
                // 因为返回值只能传递一个值，此处通过异常对象传递更多的值
                throw new OnlyPartialIdAssignedException(new IdRange(startValue, amount_));
            }
        }
        catch (Exception e)
        {
            LOG.error(e.toString());
            throw new IdGenerateException(e.getMessage());
        }
    }
}
